/**
 * Eclipse Outliner
 * Copyright (C) 2006 Jeff Mesnil
 * 
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 * 
 * http://www.apache.org/licenses/LICENSE-2.0
 * 
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License. 
 */
package net.jmesnil.outliner.ui.editors;

import java.io.OutputStream;

import net.jmesnil.opml.core.IOPML;
import net.jmesnil.opml.core.IOutline;
import net.jmesnil.opml.core.IOutlineNode;
import net.jmesnil.outliner.ui.internal.OutlineLabelProvider;
import net.jmesnil.outliner.ui.internal.OutlinerContentProvider;
import net.jmesnil.outliner.ui.internal.OutlinerEditorInput;

import org.eclipse.core.filesystem.EFS;
import org.eclipse.core.filesystem.IFileStore;
import org.eclipse.core.runtime.IPath;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.Path;
import org.eclipse.jface.viewers.CellEditor;
import org.eclipse.jface.viewers.ICellModifier;
import org.eclipse.jface.viewers.StructuredSelection;
import org.eclipse.jface.viewers.TextCellEditor;
import org.eclipse.jface.viewers.TreeSelection;
import org.eclipse.jface.viewers.TreeViewer;
import org.eclipse.swt.SWT;
import org.eclipse.swt.events.KeyAdapter;
import org.eclipse.swt.events.KeyEvent;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.FileDialog;
import org.eclipse.swt.widgets.TreeItem;
import org.eclipse.ui.IEditorInput;
import org.eclipse.ui.IEditorPart;
import org.eclipse.ui.IEditorSite;
import org.eclipse.ui.PartInitException;
import org.eclipse.ui.part.EditorPart;

public class OutlinerEditor extends EditorPart {

    public static final String ID = "net.jmesnil.outliner.ui.editors.OutlinerEditor";

    private TreeViewer viewer;

    private IOPML opml;

    private boolean dirty;

    private TextCellEditor textCellEditor;

    public OutlinerEditor() {
        super();
    }

    public void doSave(IProgressMonitor monitor) {
        IFileStore fileStore = ((OutlinerEditorInput) getEditorInput())
                .getFileStore();
        if (fileStore == null) {
            doSaveAs();
            return;
        }
        try {
            OutputStream out = fileStore.openOutputStream(EFS.NONE, null);
            out.write(opml.toXML().getBytes());
            out.flush();
            out.close();
        } catch (Exception e) {
            e.printStackTrace();
        }
        setDirty(false);
    }

    public void doSaveAs() {
        FileDialog dialog = new FileDialog(getSite().getShell(), SWT.SAVE);
        dialog.setText("Save As...");
        dialog.open();
        String name = dialog.getFileName();
        if (name != null && !name.equals("")) {
            IPath path = new Path(dialog.getFilterPath()).append(name)
                    .addFileExtension("opml");
            IFileStore fileStore = EFS.getLocalFileSystem().getStore(path);
            setPartName(fileStore.getName());
            try {
                OutputStream out = fileStore.openOutputStream(EFS.NONE, null);
                out.write(opml.toXML().getBytes());
                out.flush();
                out.close();
                ((OutlinerEditorInput) getEditorInput())
                        .setFileStore(fileStore);
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
        setDirty(false);
    }

    public void init(IEditorSite site, IEditorInput input)
            throws PartInitException {
        setSite(site);
        setInput(input);
        opml = ((OutlinerEditorInput) input).getOPML();
        IFileStore fileStore = ((OutlinerEditorInput) getEditorInput())
                .getFileStore();
        if (fileStore != null) {
            setPartName(fileStore.getName());
        } else {
            setPartName("Untitled");
        }
        setContentDescription(opml.getHead().getTitle());
        dirty = false;
    }

    public boolean isDirty() {
        return dirty;
    }

    private void setDirty(boolean dirty) {
        this.dirty = dirty;
        firePropertyChange(IEditorPart.PROP_DIRTY);
    }

    public boolean isSaveAsAllowed() {
        return true;
    }

    public void createPartControl(Composite parent) {
        viewer = new TreeViewer(parent, SWT.MULTI | SWT.H_SCROLL | SWT.V_SCROLL);
        viewer.setContentProvider(new OutlinerContentProvider());
        viewer.setLabelProvider(new OutlineLabelProvider());
        viewer.setCellModifier(new OutlineTextCellModifier());
        viewer.setColumnProperties(new String[] { "text" });
        textCellEditor = new TextCellEditor(viewer.getTree());
        textCellEditor.getControl().addKeyListener(new KeyAdapter() {
            public void keyPressed(KeyEvent e) {
                if (e.keyCode == SWT.ESC) {
                    viewer.getCellModifier().modify(
                            viewer.getTree().getSelection()[0], "text",
                            textCellEditor.getValue());
                } else if (e.keyCode == SWT.BS) {
                    if (viewer.isCellEditorActive()
                            && "".equals(textCellEditor.getValue())) {
                        viewer.cancelEditing();
                        removeSelection();
                    }
                }
            }
        });

        viewer.setCellEditors(new CellEditor[] { textCellEditor });
        viewer.getTree().addKeyListener(new KeyAdapter() {
            public void keyPressed(KeyEvent e) {
                if (e.keyCode == SWT.ESC) {
                    editSelection();
                } else if (e.keyCode == SWT.BS) {
                    if (!viewer.isCellEditorActive()) {
                        removeSelection();
                    }
                }
            }
        });
        
        viewer.setInput(opml.getBody());        
        getSite().setSelectionProvider(viewer);
    }

    public void setFocus() {
        viewer.getControl().setFocus();
    }

    private void editSelection() {
        TreeSelection selection = (TreeSelection) viewer.getSelection();
        Object element = selection.getFirstElement();
        if (element instanceof IOutline) {
            IOutline outline = (IOutline) element;
            viewer.editElement(outline, 0);
        }
    }

    private void removeSelection() {
        TreeSelection selection = (TreeSelection) viewer.getSelection();
        Object element = selection.getFirstElement();
        if (element instanceof IOutline) {
            IOutline outline = (IOutline) element;
            IOutlineNode parent = outline.getParent();
            IOutline previousSibling = outline.getPreviousSibling();
            parent.removeChild(outline);
            if (previousSibling != null) {
                viewer.setSelection(new StructuredSelection(previousSibling));
            } else {
                viewer.setSelection(new StructuredSelection(parent));
            }
            viewer.refresh(true);
        }
    }

    private final class OutlineTextCellModifier implements ICellModifier {
        public boolean canModify(Object element, String property) {
            return true;
        }

        public Object getValue(Object element, String property) {
            IOutline outline = (IOutline) element;
            return outline.getText();
        }

        public void modify(Object element, String property, Object value) {
            TreeItem item = (TreeItem) element;
            IOutline outline = (IOutline) item.getData();
            outline.setText((String) value);
            setDirty(true);
            viewer.update(outline, null);
        }
    }

    public TreeViewer getViewer() {
        return viewer;
    }

    public TextCellEditor getTextCellEditor() {
        return textCellEditor;
    }
}
